﻿using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System;

/// <summary>
///  超链接文本
///  注意：
///  此脚本的排列顺序，一定要在Text下面，并且，如果有脚本动态修改Text，则修改的脚本需要排列在Text组件前面。
///  这样执行顺序才有正常效果，比如本地化脚本，就需要排列在Text前面，便于优先修改Text
/// </summary>
[RequireComponent(typeof(Text)), DisallowMultipleComponent]
public class UIHrefLinkText : BaseMeshEffect, IPointerClickHandler
{
    /// <summary>
    /// 超链接信息类
    /// </summary>
    private class HrefInfo
    {
        public int startIndex;

        public int endIndex;

        public string name;

        public readonly List<Rect> Boxes = new List<Rect>();
    }

    /// <summary>
    /// 超链接正则
    /// </summary>
    private readonly Regex s_HrefRegex = new Regex(@"<a href=([^>\n\s]+)>(.*?)(</a>)", RegexOptions.Singleline);
    /// <summary>
    /// 用法例子，不带color标签，则超链接文本默认蓝色
    /// </summary>
    [Header("用法例子")]
    [SerializeField]
    private string tips=@"<a href=(可以是http或ID数字)><color=#ff6600>link</color></a>";

    [Header("是否显示下划线")]
    public bool useUnderline = false;
    [Header("下划线是否忽略换行符")]
    public bool ignoreBreakSign = true;
    [Header("下划线宽度"), Range(0f, 100f), SerializeField]
    private float _lineHeight = 1.5f;
    private float _lineHeightHalf = 0.75f;


    public float lineHeight
    {
        get { return this._lineHeight; }
        set
        {
            this._lineHeight = value;
            this._lineHeightHalf = value * .5f;
        }
    }
    [Header("行高是否两端扩展,否则向下扩展")]
    public bool lineHeightJustify = true;
    public float lineOffset = 0;
    public Color32 lineColor = Color.white;
    [Header("横线对齐到行的上下中间")]
    public bool lineAlignToMiddle = false;

    [Header("是否全文字段显示")]
    public bool isAllLine = false;



    private Text text;
    private UICharInfo[] characters;
    private UILineInfo[] lines;

    private char[] textChars;

    private List<UIVertex> stream = null;

    // 可视的字符个数
    private int characterCountVisible = 0;

    // 超链接信息列表  
    private List<HrefInfo> _listHrefInfos = new List<HrefInfo>();

    [Serializable]
    public class HrefClickEvent : UnityEvent<string> { }

    [Header("点击响应事件(自定义传参)")]
    [SerializeField]
    private HrefClickEvent m_OnHrefClick = new HrefClickEvent();
    /// <summary>
    /// 注册超链接点击事件
    /// </summary>
    public void RegisterClickEvent(UnityAction<string> cb)
    {
        if (cb != null) m_OnHrefClick.AddListener(cb);
    }

    public void UnRegisterClickEvent(UnityAction<string> cb)
    {
           if (cb != null) m_OnHrefClick.RemoveListener(cb);
    }

    override protected void Awake()
    {
        this.text = this.GetComponent<Text>();
    }

#if UNITY_EDITOR

    override protected void OnEnable()
    {

       if(this.text==null) this.text = this.GetComponent<Text>();
    }
#endif



    protected override void OnDestroy()
    {
        this.text = null;
        base.OnDestroy();

    }
    public void OnPointerClick(PointerEventData eventData)
    {
        Vector2 lp = Vector2.zero;
        var rectTransform = this.gameObject.transform as RectTransform;
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out lp);
        foreach (var hrefInfo in _listHrefInfos)
        {
            var boxes = hrefInfo.Boxes;

            for (var i = 0; i < boxes.Count; ++i)
            {

                if (boxes[i].Contains(lp))
                {
                    Debug.Log("Href Click: " + hrefInfo.name);
                    m_OnHrefClick?.Invoke(hrefInfo.name);
                    return;
                }
            }
        }


    }


    /// 从font纹理中获取指定字符的uv
    private Vector2 GetUnderlineCharUV()
    {
        CharacterInfo info;

        if (text.font.GetCharacterInfo('*', out info, text.fontSize, text.fontStyle))
        {
            return (info.uvBottomLeft + info.uvBottomRight + info.uvTopLeft + info.uvTopRight) * 0.25f;
        }
        else
        {
            text.font.RequestCharactersInTexture("*", text.fontSize, text.fontStyle);
            if (text.font.GetCharacterInfo('*', out info, text.fontSize, text.fontStyle))
            {
                return (info.uvBottomLeft + info.uvBottomRight + info.uvTopLeft + info.uvTopRight) * 0.25f;
            }
        }
        Debug.Log("------------->>> GetCharacterInfo  Fail: * at "+text.font.name);
        return Vector2.zero;
    }

    override public void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive() || (!this.useUnderline))
        {
            return;
        }

        // 当宽或高足够小则不处理
        if (text.rectTransform.rect.size.x <= 0 || text.rectTransform.rect.size.y <= 0) return;

#if UNITY_EDITOR
        _lineHeightHalf = this.lineHeight * .5f;
#endif


        // cachedTextGenerator是当前实际显示出来的相关信息,cachedTextGeneratorForLayout是所有布局信息(包括看不到的)
        this.characters = text.cachedTextGenerator.GetCharactersArray();
        this.lines = text.cachedTextGenerator.GetLinesArray();

        if (this.useUnderline && this.ignoreBreakSign)
        {
            this.textChars = this.text.text.ToCharArray();
        }

        // 使用characterCountVisible来得到真正显示的字符数量.characterCount会额外包含(在宽度不足)时候裁剪掉的(边缘)字符,会导致显示的下划线多一个空白的宽度
        this.characterCountVisible = text.cachedTextGenerator.characterCountVisible;

        /* stream是6个一组(对应三角形索引信息,左上角开始,顺时针)对应1个字(含空白)
            0-1
             \|
              2
            0
            |\
            3-2
            数组索引:
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...]
            对应三角形索引:
            [0, 1, 2, 2, 3, 0, 4, 5, 6, 6,  7,  4,...]
        */
        this.stream = new List<UIVertex>();

        vh.GetUIVertexStream(stream);
        vh.Clear();



        // 添加下划线
        if (useUnderline)
        {
            MatchString(this.text.text);
            if (this.isAllLine)
            {
                this.DrawAllLinesLine(vh);
            }
            else
            {
                this.DrawCustomLine(vh);
            }
        }

        vh.AddUIVertexTriangleStream(stream);

    }


    /// 获取一个字符索引所在的行
    private int GetCharInLineIndex(int charIndex)
    {
        var ei = this.lines.Length - 1;
        for (int i = 0; i < ei; i++)
        {
            var line = lines[i];
            if (charIndex >= line.startCharIdx && charIndex < lines[i + 1].startCharIdx) return i;
        }

        // 是否在最后一行
        if (charIndex >= lines[ei].startCharIdx && charIndex < this.characters.Length) return ei;

        return -1;
    }
    // 显示所有下划线
    private void DrawAllLinesLine(VertexHelper vh)
    {
        var uv0 = this.GetUnderlineCharUV();
        for (int i = 0; i < this.lines.Length; i++)
        {
            var line = this.lines[i];
            var endIndex = 0;

            if (i + 1 < this.lines.Length)
            {
                // 通过下一行的起始索引减1得到这一行最后一个字符索引位置
                var nextLineStartCharIdx = this.lines[i + 1].startCharIdx;
                // 与本行的相同,当文本宽度只够容纳一个字的时候,unity会产生一个空行,要排除改行
                if (nextLineStartCharIdx == this.lines[i].startCharIdx) continue;

                endIndex = nextLineStartCharIdx - 1;
            }
            else
            {
                // 最后一行的最后字符索引位置
                if (this.characterCountVisible == 0) continue;
                endIndex = this.characterCountVisible - 1;
            }

            var bottomY = this.GetLineBottomY(i);

            var firstCharOff = line.startCharIdx;
            this.AddUnderlineVertTriangle(vh, line.startCharIdx, endIndex, firstCharOff, bottomY, uv0);
            AddUnderlineBound(line.startCharIdx, endIndex, _listHrefInfos[0]);
        }
    }
    // 显示自定义起止点下划线
    private void DrawCustomLine(VertexHelper vh)
    {
        var uv0 = this.GetUnderlineCharUV();
        var charsMaxIndex = this.characterCountVisible - 1;

        for (int i = 0; i < this._listHrefInfos.Count; i++)
        {
            var v2 = this._listHrefInfos[i];
            var startIndex = v2.startIndex;
            var endIndex = v2.endIndex;

            if (endIndex < startIndex)
            {
                startIndex = v2.endIndex;
                endIndex = v2.startIndex;
            }

            if (startIndex < 0) startIndex = 0;
            if (endIndex > charsMaxIndex) endIndex = charsMaxIndex;

            if (startIndex >= this.characterCountVisible) continue;

            var lineIndex0 = this.GetCharInLineIndex(startIndex);
            var lineIndex1 = this.GetCharInLineIndex(endIndex);
            if (lineIndex0 != lineIndex1)
            {
                // 跨行
                for (int j = lineIndex0; j <= lineIndex1; j++)
                {
                    var bottomY = this.GetLineBottomY(j);
                    var lineStartCharIndex = startIndex;
                    var lineEndCharIndex = endIndex;
                    if (j == lineIndex0)
                    {
                        // 第一行,从指定起始字索引到改行末尾字索引(改行末尾索引是下一行的起始索引-1得到)
                        lineEndCharIndex = lines[j + 1].startCharIdx - 1;
                    }
                    else if (j == lineIndex1)
                    {
                        // 最后一行,从改行起始字索引到指定字索引
                        lineStartCharIndex = lines[j].startCharIdx;
                    }
                    else
                    {
                        // 中间行,从改行起始字所以到该行末尾字索引
                        lineStartCharIndex = lines[j].startCharIdx;
                        lineEndCharIndex = lines[j + 1].startCharIdx - 1;
                    }

                    var firstCharOff = lines[j].startCharIdx;
                    this.AddUnderlineVertTriangle(vh, lineStartCharIndex, lineEndCharIndex, firstCharOff, bottomY, uv0);
                    AddUnderlineBound(lineStartCharIndex, lineEndCharIndex, v2);
                }
            }
            else
            {
                // 在同一行
                var bottomY = this.GetLineBottomY(lineIndex0);
                var firstCharOff = lines[lineIndex0].startCharIdx;
                this.AddUnderlineVertTriangle(vh, startIndex, endIndex, firstCharOff, bottomY, uv0);
                AddUnderlineBound(startIndex, endIndex, v2);

            }
        }
    }
    private float GetLineBottomY(int lineIndex)
    {
        UILineInfo line = this.lines[lineIndex];
        var bottomY = line.topY - (lineAlignToMiddle ? line.height * .5f : line.height) - lineOffset;

        // bottomY是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
        bottomY /= this.text.pixelsPerUnit;


        return bottomY;
    }

    private Vector2 GetCharCursorPos(int charIdx, float firstCharOff)
    {
        var charInfo = this.characters[charIdx];
        var cursorPos = charInfo.cursorPos;
        // cursorPos是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
        cursorPos /= this.text.pixelsPerUnit;

        return cursorPos;
    }
    private UIVertex[] underlineUIVertexs = new UIVertex[6];
    private void AddUnderlineVertTriangle(VertexHelper vh, int startIndex, int endIndex, float firstCharOff, float bottomY, Vector2 uv0)
    {
        if (this.ignoreBreakSign && this.textChars[endIndex] == '\n')
        {
            // 跳过换行符
            endIndex--;
        }
        if (endIndex < startIndex) return;

        // 左上
        var pos0 = new Vector3(this.GetCharCursorPos(startIndex, firstCharOff).x, bottomY + (this.lineHeightJustify ? this._lineHeightHalf : 0), 0);

        // 左下, 向下扩展
        var pos1 = new Vector3(pos0.x, pos0.y - this.lineHeight, 0);

        // 右下. charWidth是原始大小下的信息,但文本在不同分辨率下会被进一步缩放处理,所以要将比例带入计算
        var pos2 = new Vector3(this.GetCharCursorPos(endIndex, firstCharOff).x + characters[endIndex].charWidth / this.text.pixelsPerUnit, pos1.y, 0);

        // 右上
        var pos3 = new Vector3(pos2.x, pos0.y, 0);

        // 按照stream存储的规范,构建6个顶点: 左上和右下是2个三角形的重叠, 
        UIVertex vert = UIVertex.simpleVert;
        vert.color = this.lineColor;
        vert.uv0 = uv0;

        vert.position = pos0;
        underlineUIVertexs[0] = vert;
        underlineUIVertexs[3] = vert;

        vert.position = pos1;
        underlineUIVertexs[5] = vert;

        vert.position = pos2;
        underlineUIVertexs[2] = vert;
        underlineUIVertexs[4] = vert;

        vert.position = pos3;
        underlineUIVertexs[1] = vert;

        stream.AddRange(underlineUIVertexs);
        // stream.Add(underlineUIVertexs[0]);
        // stream.Add(underlineUIVertexs[3]);
        // stream.Add(underlineUIVertexs[2]);
        // stream.Add(underlineUIVertexs[0]);
        // stream.Add(underlineUIVertexs[2]);
        // stream.Add(underlineUIVertexs[1]);

    }

    private void AddUnderlineBound(int startIndex, int endIndex, HrefInfo hrefInfo)
    {

        var startpos = this.GetCharCursorPos(startIndex, 0);
        var endpos = this.GetCharCursorPos(endIndex, 0);

        var width = characters[endIndex].charWidth / this.text.pixelsPerUnit;
        var height = lines[0].height / this.text.pixelsPerUnit;

        endpos = new Vector2(endpos.x + width, endpos.y - height);

        var bounds = new Bounds(startpos, Vector3.zero);
        bounds.Encapsulate(endpos);
        hrefInfo.Boxes.Add(new Rect(bounds.min, bounds.size));

    }
    private void MatchString(string src)
    {
        _listHrefInfos.Clear();

        if (this.isAllLine)
        {
            _listHrefInfos.Add(new HrefInfo());
            return;
        }

        MatchCollection matchCollection = s_HrefRegex.Matches(src);
        foreach (object obj in matchCollection)
        {
            Match match = (Match)obj;

            // Debug.Log("---1-----> " + match.Groups[1].Value + "," + match.Groups[1].Index);
            //Debug.Log("----2----> " + match.Groups[2].Value + "," + match.Groups[2].Index);
            var hrefInfo = new HrefInfo
            {
                startIndex = match.Groups[2].Index,//下划线字符起始下标
                endIndex = match.Groups[2].Index + match.Groups[2].Length - 1,//下划线字符结束下标
                name = match.Groups[1].Value //href链接地址，可以是ID数字，www等，点击事件判断
            };
            _listHrefInfos.Add(hrefInfo);
           // Debug.Log("----2----> " + match.Groups[2].Value + "," + hrefInfo.startIndex + "," + hrefInfo.endIndex);
        }

    }

    #region UNITY_EDITOR
#if UNITY_EDITOR

    //辅助线框
    Vector3[] _textWolrdVertexs = new Vector3[4];
    private void OnDrawGizmos()
    {
        var rectTransform = this.gameObject.transform as RectTransform;
        //text
        //rectTransform.GetWorldCorners(_textWolrdVertexs);
        //GizmosDrawLine(Color.white, _textWolrdVertexs);

        //preferred size
        //Vector2 pivot = Text.GetTextAnchorPivot(text.alignment);
        //Rect rect = new Rect();
        //Vector2 size = rectTransform.sizeDelta - new Vector2(text.preferredWidth, text.preferredHeight);
        //rect.position = new Vector2(pivot.x * size.x, pivot.y * size.y) - new Vector2(rectTransform.sizeDelta.x * rectTransform.pivot.x, rectTransform.sizeDelta.y * rectTransform.pivot.y);
        //rect.width = text.preferredWidth;
        //rect.height = text.preferredHeight;
        //_textWolrdVertexs[0] = TransformPoint2World(transform, new Vector3(rect.x, rect.y));
        //_textWolrdVertexs[1] = TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y));
        //_textWolrdVertexs[2] = TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y + rect.height));
        //_textWolrdVertexs[3] = TransformPoint2World(transform, new Vector3(rect.x, rect.y + rect.height));
        //GizmosDrawLine(Color.blue, _textWolrdVertexs);

        //href
        Rect rect = new Rect();
        for (int i = 0; i < _listHrefInfos.Count; i++)
        {
            for (int j = 0; j < _listHrefInfos[i].Boxes.Count; j++)
            {
                rect = _listHrefInfos[i].Boxes[j];
                _textWolrdVertexs[0] = TransformPoint2World(transform, rect.position);
                _textWolrdVertexs[1] = TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y));
                _textWolrdVertexs[2] = TransformPoint2World(transform, new Vector3(rect.x + rect.width, rect.y + rect.height));
                _textWolrdVertexs[3] = TransformPoint2World(transform, new Vector3(rect.x, rect.y + rect.height));

                GizmosDrawLine(Color.green, _textWolrdVertexs);
            }
        }
    }

    //划线
    private void GizmosDrawLine(Color color, Vector3[] pos)
    {
        Gizmos.color = color;

        Gizmos.DrawLine(pos[0], pos[1]);
        Gizmos.DrawLine(pos[1], pos[2]);
        Gizmos.DrawLine(pos[2], pos[3]);
        Gizmos.DrawLine(pos[3], pos[0]);
    }
    /// <summary>
    /// 获取Transform的世界坐标
    /// </summary>
    /// <param name="rectTransform"></param>
    /// <param name="canvas"></param>
    /// <returns></returns>
    public Vector3 TransformPoint2World(Transform transform, Vector3 point)
    {
        return transform.localToWorldMatrix.MultiplyPoint(point);
    }

    /// <summary>
    /// 获取Transform的本地坐标
    /// </summary>
    /// <param name="transform"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    public Vector3 TransformWorld2Point(Transform transform, Vector3 point)
    {
        return transform.worldToLocalMatrix.MultiplyPoint(point);
    }
#endif
    #endregion

}
